//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#ifndef NJ_GENERIC_STATIC_INTERFACE_H
#	define NJ_GENERIC_STATIC_INTERFACE_H

#	include "NjNinjaSyncLib.h"
#	include "NjRefObject.h"
#	include "NjSmartPtr.h"

	/// \brief This class implements common features of several interfaces to access to operating system functionalities in a cross-platform manner
	class NjGenericStaticInterface
	{
	public:
		/// \brief The Interface implementation
		class Implementation : public NjRefObject
		{
			friend class NjGenericStaticInterface;

		public:
			NJ_TRIVIAL_VIRTUAL_DESTRUCTOR(Implementation);

		protected:
			/// \brief Implementation default constructor
			Implementation();

			/// \brief Called after the implementation is assigned to the interface
			virtual void OnSetImplementation();

			/// \brief Called before the implementation is unassigned from the interface
			virtual void OnUnsetImplementation();

			/// \brief Return NJ_TRUE if the implementation is currently assigned to the interface
			NjBool IsImplementationSet() const;

		private:
			/// \brief Set to NJ_TRUE if the implementation is currently assigned to the interface
			NjBool m_bIsImplementationSet;

			NJ_DISABLE_COPY(Implementation);
		};

		/// \brief Callbacks that can be registered to be notified when an implementation is attacched/detacched from the interface
		class OnImplementationCallBack
		{
			friend class NjGenericStaticInterface;

		protected:
			NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(OnImplementationCallBack);
			NJ_TRIVIAL_VIRTUAL_DESTRUCTOR(OnImplementationCallBack);

			/// \brief Called after \em pImplementation is assigned to the interface
			virtual void OnSetImplementation(Implementation * pImplementation) = 0;

			/// \brief Called before \em pImplementatio is unassigned from the interface
			virtual void OnUnsetImplementation(Implementation * pImplementation) = 0;

		private:
			/// \brief The next implementation callback
			OnImplementationCallBack * m_pNext;

			NJ_DISABLE_COPY(OnImplementationCallBack);
		};

	protected:
		/// \brief Private class for internal use
		class OnImplementationChangeEntry;

		/// \brief Helper method to notify that \em pImplementation is going to be unassigned from the interface
		static void OnUnsetImplementation(OnImplementationCallBack * pHead, Implementation * pImplementation);

		/// \brief Helper method to notify that \em pImplementation has been assigned from the interface
		static void OnSetImplementation(OnImplementationCallBack * pHead, Implementation * pImplementation);

		/// \brief Helper method to register a callback to the callbacks list
		static void AddImplementationCallBack(OnImplementationCallBack * * ppHead, OnImplementationCallBack * pEntry);

		/// \brief Helper method to unregister a callback to the callbacks list
		static void RemoveImplementationCallBack(OnImplementationCallBack * * ppHead, OnImplementationCallBack * pEntry);

	private:
		NJ_DISABLE_INSTANCE(NjGenericStaticInterface);
	};

/// \brief Declares standard interface methods
///
/// SetImplementation - used to assign an implementation to the interface; GetImplementation used to get the active implementation;
/// AddImplementationCallBack - used to register a callback; RemoveImplementationCallBack - used to unregister a callback
#	define NJ_DECLARE_STATIC_INTERFACE_METHODS()																					\
	private:																														\
		class StaticAttributes;																										\
																																	\
	public:																															\
		static void SetImplementation(Implementation * pImplementation);															\
		static Implementation * GetImplementation();																				\
		static void AddImplementationCallBack(OnImplementationCallBack * pCallBack);												\
		static void RemoveImplementationCallBack(OnImplementationCallBack * pCallBack);

/// \brief Defines standard interface methods
#	define NJ_DEFINE_STATIC_INTERFACE_METHODS(tClass)																									\
		class tClass::StaticAttributes																													\
		{																																				\
		public:																																			\
			static NjSmartPtr<Implementation> ms_spStaticInterfaceImplementation;																		\
			static OnImplementationCallBack * ms_pCallBacks;																							\
		private:																																		\
			NJ_DISABLE_INSTANCE(StaticAttributes);																										\
		};																																				\
																																						\
		void tClass::SetImplementation(Implementation * pImplementation)																				\
		{																																				\
			if (StaticAttributes::ms_spStaticInterfaceImplementation!=NJ_NULL)																			\
				NjGenericStaticInterface::OnUnsetImplementation(StaticAttributes::ms_pCallBacks, StaticAttributes::ms_spStaticInterfaceImplementation);	\
																																						\
			StaticAttributes::ms_spStaticInterfaceImplementation=pImplementation;																		\
																																						\
			if (StaticAttributes::ms_spStaticInterfaceImplementation!=NJ_NULL)																			\
				NjGenericStaticInterface::OnSetImplementation(StaticAttributes::ms_pCallBacks, StaticAttributes::ms_spStaticInterfaceImplementation);	\
		}																																				\
																																						\
		tClass::Implementation * tClass::GetImplementation()																							\
		{																																				\
			return StaticAttributes::ms_spStaticInterfaceImplementation;																				\
		}																																				\
																																						\
		void tClass::AddImplementationCallBack(OnImplementationCallBack * pCallBack)																	\
		{																																				\
			return NjGenericStaticInterface::AddImplementationCallBack(&StaticAttributes::ms_pCallBacks, pCallBack);									\
		}																																				\
																																						\
		void tClass::RemoveImplementationCallBack(OnImplementationCallBack * pCallBack)																	\
		{																																				\
			return NjGenericStaticInterface::RemoveImplementationCallBack(&StaticAttributes::ms_pCallBacks, pCallBack);									\
		}																																				\
																																						\
		NjSmartPtr<tClass::Implementation> tClass::StaticAttributes::ms_spStaticInterfaceImplementation;												\
		NjGenericStaticInterface::OnImplementationCallBack * tClass::StaticAttributes::ms_pCallBacks(NJ_NULL);

#	include "NjGenericStaticInterface_inline.h"

#endif // NJ_GENERIC_STATIC_INTERFACE_H
